﻿using System;
using System.ServiceModel;

using Coding4Fun.Cannon.Core.Settings;
using Coding4Fun.Cannon.Robot;

namespace Coding4Fun.Cannon.Wcf
{
	// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "AimService" in both code and config file together.
	[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
	public class AimService : IAimService, IDisposable
	{
		private static AimData _aimData;

		private static PhidgetServo Servo
		{
			get
			{
				if (_servo == null)
				{
					var settingService = new SettingsService();

					double minTiltReturnValue;
					double maxTiltReturnValue;

					TiltCorrectionMinMax(settingService.GetAimTiltMinAngle(), settingService.GetAimTiltMaxAngle(), out minTiltReturnValue, out maxTiltReturnValue);

					var settings = new ServoSettings
					{
						PanAcceleration = 1500,
						PanMinPosition = settingService.GetAimPanMinAngle(),
						PanMaxPosition = settingService.GetAimPanMaxAngle(),
						PanNormalVelocity = settingService.GetAimPanVelocity(),
						TiltAcceleration = 1500,
						TiltMinPosition = minTiltReturnValue,
						TiltMaxPosition = maxTiltReturnValue,
						TiltNormalVelocity = settingService.GetAimTiltVelocity()
					};

					_servo = new PhidgetServo(settings);
				}

				return _servo;
			}
		}
		private static PhidgetServo _servo;
		public void Dispose()
		{
			if (_servo != null)
				Servo.Dispose();
			_servo = null;
		}

		public AimData Reset()
		{
			var pan = (GetPanMinAngle() + GetPanMaxAngle()) / 2.0;
			var tilt = (GetTiltMinAngle() + GetTiltMaxAngle()) / 2.0;

			return SetPanAndTiltAngles(pan, tilt);
		}
		
		public AimData SetPanAndTiltRandomly()
		{
			Servo.SetPanAndTiltRandomly();
			
			UpdateAimData();
			return _aimData;
		}

		public AimData SetAimFiringTiltAngle()
		{
			var settingService = new SettingsService();

			return SetTiltAngle(settingService.GetAimFiringTiltAngle());
		}

		public AimData SetPanAndTiltAngles(double panAngle, double tiltAngle)
		{
			Servo.SetAngle(-1 * panAngle, TiltCorrection(tiltAngle));

			UpdateAimData();
			return _aimData;
		}

		public AimData SetPanAngle(double angle)
		{
			Servo.SetPanAngle(-1 * angle);

			UpdateAimData();
			return _aimData;
		}

		public AimData SetTiltAngle(double angle)
		{
			Servo.SetTiltAngle(TiltCorrection(angle));

			UpdateAimData();
			return _aimData;
		}

		public AimData SetByAimData(AimData data)
		{
			return SetPanAndTiltAngles(data.PanAngle, data.TiltAngle);
		}

		public AimData GetAimData()
		{
			return _aimData;
		}

		private void UpdateAimData()
		{
			_aimData.PanAngle = GetCurrentPanAngle();
			_aimData.TiltAngle = GetCurrentTiltAngle();
		}

		public double GetCurrentPanAngle()
		{
			return Servo.PanAngle;	
		}

		public double GetCurrentTiltAngle()
		{
			return Servo.TiltAngle;	
		}

		// settings part
		internal double GetPanVelocity()
		{
			return Servo.PanVelocityLimit;
		}

		internal double GetTiltVelocity()
		{
			return Servo.TiltVelocityLimit;
		}

		internal void SetPanVelocity(double value)
		{
			Servo.PanVelocityLimit = value;
		}

		internal void SetTiltVelocity(double value)
		{
			Servo.TiltVelocityLimit = value;
		}

		internal double GetPanMinAngle()
		{
			return Servo.PanMinPosition;
		}

		internal double GetPanMaxAngle()
		{
			return Servo.PanMaxPosition;
		}

		internal double GetTiltMinAngle()
		{
			return Servo.TiltMinPosition;
		}

		internal double GetTiltMaxAngle()
		{
			return Servo.TiltMaxPosition;
		}

		internal void SetPanMinAndMaxAngles(double minAngle, double maxAngle)
		{
			Servo.PanMinPosition = minAngle;
			Servo.PanMaxPosition = maxAngle;
		}

		internal void SetTiltMinAndMaxAngles(double minAngle, double maxAngle)
		{
			double minReturnValue;
			double maxReturnValue;

			TiltCorrectionMinMax(minAngle, maxAngle, out minReturnValue, out maxReturnValue);
			Servo.TiltMinPosition = minReturnValue;
			Servo.TiltMaxPosition = maxReturnValue;
		}

		private static double TiltCorrection(double value)
		{
			return value - 180;
		}

		private static void TiltCorrectionMinMax(double minValue, double maxValue, out double minReturnValue, out double maxReturnValue)
		{
			minReturnValue = TiltCorrection(minValue);
			maxReturnValue = TiltCorrection(maxValue);

			if (minValue <= maxValue)
				return;

			minValue = minReturnValue;
			minReturnValue = maxReturnValue;
			maxReturnValue = minValue;
		}
	}
}
